/*
 * PROJECT:         iFreeOS HAL
 * LICENSE:         BSD - See COPYING.ARM in the top level directory
 * FILE:            hal/halx86/generic/pic.c
 * PURPOSE:         HAL PIC Management and Control Code
 * PROGRAMMERS:     iFreeOS Portable Systems Group
 */

/* INCLUDES *******************************************************************/

#include <hal.h>
#define NDEBUG
#include <debug.h>

/* GLOBALS ********************************************************************/

#ifndef _MINIHAL_
/*
 * This table basically keeps track of level vs edge triggered interrupts.
 * Windows has 250+ entries, but it seems stupid to replicate that since the PIC
 * can't actually have that many.
 *
 * When a level interrupt is registered, the respective pointer in this table is
 * modified to point to a dimiss routine for level interrupts instead.
 *
 * The other thing this table does is special case IRQ7, IRQ13 and IRQ15:
 *
 * - If an IRQ line is deasserted before it is acknowledged due to a noise spike
 *   generated by an expansion device (since the IRQ line is low during the 1st
 *   acknowledge bus cycle), the i8259 will keep the line low for at least 100ns
 *   When the spike passes, a pull-up resistor will return the IRQ line to high.
 *   Since the PIC requires the input be high until the first acknowledge, the
 *   i8259 knows that this was a spurious interrupt, and on the second interrupt
 *   acknowledge cycle, it reports this to the CPU. Since no valid interrupt has
 *   actually happened Intel hardcoded the chip to report IRQ7 on the master PIC
 *   and IRQ15 on the slave PIC (IR7 either way).
 *
 *   "ISA System Architecture", 3rd Edition, states that these cases should be
 *   handled by reading the respective Interrupt Service Request (ISR) bits from
 *   the affected PIC, and validate whether or not IR7 is set. If it isn't, then
 *   the interrupt is spurious and should be ignored.
 *
 *   Note that for a spurious IRQ15, we DO have to send an EOI to the master for
 *   IRQ2 since the line was asserted by the slave when it received the spurious
 *   IRQ15!
 *
 * - When the 80287/80387 math co-processor generates an FPU/NPX trap, this is
 *   connected to IRQ13, so we have to clear the busy latch on the NPX port.
 */
PHAL_DISMISS_INTERRUPT HalpSpecialDismissTable[16] =
{
    HalpDismissIrqGeneric,
    HalpDismissIrqGeneric,
    HalpDismissIrqGeneric,
    HalpDismissIrqGeneric,
    HalpDismissIrqGeneric,
    HalpDismissIrqGeneric,
    HalpDismissIrqGeneric,
    HalpDismissIrq07,
    HalpDismissIrqGeneric,
    HalpDismissIrqGeneric,
    HalpDismissIrqGeneric,
    HalpDismissIrqGeneric,
    HalpDismissIrqGeneric,
    HalpDismissIrq13,
    HalpDismissIrqGeneric,
    HalpDismissIrq15
};

/*
 * These are the level IRQ dismissal functions that get copied in the table
 * above if the given IRQ is actually level triggered.
 */
PHAL_DISMISS_INTERRUPT HalpSpecialDismissLevelTable[16] =
{
    HalpDismissIrqLevel,
    HalpDismissIrqLevel,
    HalpDismissIrqLevel,
    HalpDismissIrqLevel,
    HalpDismissIrqLevel,
    HalpDismissIrqLevel,
    HalpDismissIrqLevel,
    HalpDismissIrq07Level,
    HalpDismissIrqLevel,
    HalpDismissIrqLevel,
    HalpDismissIrqLevel,
    HalpDismissIrqLevel,
    HalpDismissIrqLevel,
    HalpDismissIrq13Level,
    HalpDismissIrqLevel,
    HalpDismissIrq15Level
};

/* This table contains the static x86 PIC mapping between IRQLs and IRQs */
ULONG KiI8259MaskTable[32] =
{
#if defined(__GNUC__) && \
    (__GNUC__ * 100 + __GNUC_MINOR__ >= 404)
    /*
     * It Device IRQLs only start at 4 or higher, so these are just software
     * IRQLs that don't really change anything on the hardware
     */
    0b00000000000000000000000000000000, /* IRQL 0 */
    0b00000000000000000000000000000000, /* IRQL 1 */
    0b00000000000000000000000000000000, /* IRQL 2 */
    0b00000000000000000000000000000000, /* IRQL 3 */

    /*
     * These next IRQLs are actually useless from the PIC perspective, because
     * with only 2 PICs, the mask you can send them is only 8 bits each, for 16
     * bits total, so these IRQLs are masking off a phantom PIC.
     */
    0b11111111100000000000000000000000, /* IRQL 4 */
    0b11111111110000000000000000000000, /* IRQL 5 */
    0b11111111111000000000000000000000, /* IRQL 6 */
    0b11111111111100000000000000000000, /* IRQL 7 */
    0b11111111111110000000000000000000, /* IRQL 8 */
    0b11111111111111000000000000000000, /* IRQL 9 */
    0b11111111111111100000000000000000, /* IRQL 10 */
    0b11111111111111110000000000000000, /* IRQL 11 */

    /*
     * Okay, now we're finally starting to mask off IRQs on the slave PIC, from
     * IRQ15 to IRQ8. This means the higher-level IRQs get less priority in the
     * IRQL sense.
     */
    0b11111111111111111000000000000000, /* IRQL 12 */
    0b11111111111111111100000000000000, /* IRQL 13 */
    0b11111111111111111110000000000000, /* IRQL 14 */
    0b11111111111111111111000000000000, /* IRQL 15 */
    0b11111111111111111111100000000000, /* IRQL 16 */
    0b11111111111111111111110000000000, /* IRQL 17 */
    0b11111111111111111111111000000000, /* IRQL 18 */
    0b11111111111111111111111000000000, /* IRQL 19 */

    /*
     * Now we mask off the IRQs on the master. Notice the 0 "droplet"? You might
     * have also seen that IRQL 18 and 19 are essentially equal as far as the
     * PIC is concerned. That bit is actually IRQ8, which happens to be the RTC.
     * The RTC will keep firing as long as we don't reach PROFILE_LEVEL which
     * actually kills it. The RTC clock (unlike the system clock) is used by the
     * profiling APIs in the HAL, so that explains the logic.
     */
    0b11111111111111111111111010000000, /* IRQL 20 */
    0b11111111111111111111111011000000, /* IRQL 21 */
    0b11111111111111111111111011100000, /* IRQL 22 */
    0b11111111111111111111111011110000, /* IRQL 23 */
    0b11111111111111111111111011111000, /* IRQL 24 */
    0b11111111111111111111111011111000, /* IRQL 25 */
    0b11111111111111111111111011111010, /* IRQL 26 */
    0b11111111111111111111111111111010, /* IRQL 27 */

    /*
     * IRQL 24 and 25 are actually identical, so IRQL 28 is actually the last
     * IRQL to modify a bit on the master PIC. It happens to modify the very
     * last of the IRQs, IRQ0, which corresponds to the system clock interval
     * timer that keeps track of time (the Windows heartbeat). We only want to
     * turn this off at a high-enough IRQL, which is why IRQLs 24 and 25 are the
     * same to give this guy a chance to come up higher. Note that IRQL 28 is
     * called CLOCK2_LEVEL, which explains the usage we just explained.
     */
    0b11111111111111111111111111111011, /* IRQL 28 */

    /*
     * We have finished off with the PIC so there's nothing left to mask at the
     * level of these IRQLs, making them only logical IRQLs on x86 machines.
     * Note that we have another 0 "droplet" you might've caught since IRQL 26.
     * In this case, it's the 2nd bit that never gets turned off, which is IRQ2,
     * the cascade IRQ that we use to bridge the slave PIC with the master PIC.
     * We never want to turn it off, so no matter the IRQL, it will be set to 0.
     */
    0b11111111111111111111111111111011, /* IRQL 29 */
    0b11111111111111111111111111111011, /* IRQL 30 */
    0b11111111111111111111111111111011  /* IRQL 31 */
#else
    0,                             /* IRQL 0 */
    0,                             /* IRQL 1 */
    0,                             /* IRQL 2 */
    0,                             /* IRQL 3 */
    0xFF800000,                    /* IRQL 4 */
    0xFFC00000,                    /* IRQL 5 */
    0xFFE00000,                    /* IRQL 6 */
    0xFFF00000,                    /* IRQL 7 */
    0xFFF80000,                    /* IRQL 8 */
    0xFFFC0000,                    /* IRQL 9 */
    0xFFFE0000,                    /* IRQL 10 */
    0xFFFF0000,                    /* IRQL 11 */
    0xFFFF8000,                    /* IRQL 12 */
    0xFFFFC000,                    /* IRQL 13 */
    0xFFFFE000,                    /* IRQL 14 */
    0xFFFFF000,                    /* IRQL 15 */
    0xFFFFF800,                    /* IRQL 16 */
    0xFFFFFC00,                    /* IRQL 17 */
    0xFFFFFE00,                    /* IRQL 18 */
    0xFFFFFE00,                    /* IRQL 19 */
    0xFFFFFE80,                    /* IRQL 20 */
    0xFFFFFEC0,                    /* IRQL 21 */
    0xFFFFFEE0,                    /* IRQL 22 */
    0xFFFFFEF0,                    /* IRQL 23 */
    0xFFFFFEF8,                    /* IRQL 24 */
    0xFFFFFEF8,                    /* IRQL 25 */
    0xFFFFFEFA,                    /* IRQL 26 */
    0xFFFFFFFA,                    /* IRQL 27 */
    0xFFFFFFFB,                    /* IRQL 28 */
    0xFFFFFFFB,                    /* IRQL 29 */
    0xFFFFFFFB,                    /* IRQL 30 */
    0xFFFFFFFB                     /* IRQL 31 */
#endif
};

/* This table indicates which IRQs, if pending, can preempt a given IRQL level */
ULONG FindHigherIrqlMask[32] =
{
#if defined(__GNUC__) && \
    (__GNUC__ * 100 + __GNUC_MINOR__ >= 404)
    /*
     * Software IRQLs, at these levels all hardware interrupts can preempt.
     * Each higher IRQL simply enables which software IRQL can preempt the
     * current level.
     */
    0b11111111111111111111111111111110, /* IRQL 0 */
    0b11111111111111111111111111111100, /* IRQL 1 */
    0b11111111111111111111111111111000, /* IRQL 2 */

    /*
     * IRQL3 means only hardware IRQLs can now preempt. These last 4 zeros will
     * then continue throughout the rest of the list, trickling down.
     */
    0b11111111111111111111111111110000, /* IRQL 3 */

    /*
     * Just like in the previous list, these masks don't really mean anything
     * since we've only got two PICs with 16 possible IRQs total
     */
    0b00000111111111111111111111110000, /* IRQL 4 */
    0b00000011111111111111111111110000, /* IRQL 5 */
    0b00000001111111111111111111110000, /* IRQL 6 */
    0b00000000111111111111111111110000, /* IRQL 7 */
    0b00000000011111111111111111110000, /* IRQL 8 */
    0b00000000001111111111111111110000, /* IRQL 9 */
    0b00000000000111111111111111110000, /* IRQL 10 */

    /*
     * Now we start progressivly limiting which slave PIC interrupts have the
     * right to preempt us at each level.
     */
    0b00000000000011111111111111110000, /* IRQL 11 */
    0b00000000000001111111111111110000, /* IRQL 12 */
    0b00000000000000111111111111110000, /* IRQL 13 */
    0b00000000000000011111111111110000, /* IRQL 14 */
    0b00000000000000001111111111110000, /* IRQL 15 */
    0b00000000000000000111111111110000, /* IRQL 16 */
    0b00000000000000000011111111110000, /* IRQL 17 */
    0b00000000000000000001111111110000, /* IRQL 18 */
    0b00000000000000000001111111110000, /* IRQL 19 */

    /*
     * Also recall from the earlier table that IRQL 18/19 are treated the same
     * in order to spread the masks better thoughout the 32 IRQLs and to reflect
     * the fact that some bits will always stay on until much higher IRQLs since
     * they are system-critical. One such example is the 1 bit that you start to
     * see trickling down here. This is IRQ8, the RTC timer used for profiling,
     * so it will always preempt until we reach PROFILE_LEVEL.
     */
    0b00000000000000000001011111110000, /* IRQL 20 */
    0b00000000000000000001001111110000, /* IRQL 20 */
    0b00000000000000000001000111110000, /* IRQL 22 */
    0b00000000000000000001000011110000, /* IRQL 23 */
    0b00000000000000000001000001110000, /* IRQL 24 */
    0b00000000000000000001000000110000, /* IRQL 25 */
    0b00000000000000000001000000010000, /* IRQL 26 */

    /* At this point, only the clock (IRQ0) can still preempt... */
    0b00000000000000000000000000010000, /* IRQL 27 */

    /* And any higher than that there's no relation with hardware PICs anymore */
    0b00000000000000000000000000000000, /* IRQL 28 */
    0b00000000000000000000000000000000, /* IRQL 29 */
    0b00000000000000000000000000000000, /* IRQL 30 */
    0b00000000000000000000000000000000  /* IRQL 31 */
#else
    0xFFFFFFFE,                   /* IRQL  0 */
    0xFFFFFFFC,                   /* IRQL 1 */
    0xFFFFFFF8,                   /* IRQL 2 */
    0xFFFFFFF0,                   /* IRQL 3 */
    0x7FFFFF0,                    /* IRQL 4 */
    0x3FFFFF0,                    /* IRQL 5 */
    0x1FFFFF0,                    /* IRQL 6 */
    0x0FFFFF0,                    /* IRQL 7 */
    0x7FFFF0,                     /* IRQL 8 */
    0x3FFFF0,                     /* IRQL 9 */
    0x1FFFF0,                     /* IRQL 10 */
    0x0FFFF0,                     /* IRQL 11 */
    0x7FFF0,                      /* IRQL 12 */
    0x3FFF0,                      /* IRQL 13 */
    0x1FFF0,                      /* IRQL 14 */
    0x0FFF0,                      /* IRQL 15 */
    0x7FF0,                       /* IRQL 16 */
    0x3FF0,                       /* IRQL 17 */
    0x1FF0,                       /* IRQL 18 */
    0x1FF0,                       /* IRQL 19 */
    0x17F0,                       /* IRQL 20 */
    0x13F0,                       /* IRQL 21 */
    0x11F0,                       /* IRQL 22 */
    0x10F0,                       /* IRQL 23 */
    0x1070,                       /* IRQL 24 */
    0x1030,                       /* IRQL 25 */
    0x1010,                       /* IRQL 26 */
    0x10,                         /* IRQL 27 */
    0,                            /* IRQL 28 */
    0,                            /* IRQL 29 */
    0,                            /* IRQL 30 */
    0                             /* IRQL 31 */
#endif
};

/* Denotes minimum required IRQL before we can process pending SW interrupts */
KIRQL SWInterruptLookUpTable[8] =
{
    PASSIVE_LEVEL,                 /* IRR 0 */
    PASSIVE_LEVEL,                 /* IRR 1 */
    APC_LEVEL,                     /* IRR 2 */
    APC_LEVEL,                     /* IRR 3 */
    DISPATCH_LEVEL,                /* IRR 4 */
    DISPATCH_LEVEL,                /* IRR 5 */
    DISPATCH_LEVEL,                /* IRR 6 */
    DISPATCH_LEVEL                 /* IRR 7 */
};

#if defined(__GNUC__)

#define HalpDelayedHardwareInterrupt(x)                             \
    VOID HalpHardwareInterrupt##x(VOID);                            \
    VOID                                                            \
    HalpHardwareInterrupt##x(VOID)                                  \
    {                                                               \
        asm volatile ("int $%c0\n"::"i"(PRIMARY_VECTOR_BASE + x));  \
    }

#elif defined(_MSC_VER)

#define HalpDelayedHardwareInterrupt(x)                             \
    VOID HalpHardwareInterrupt##x(VOID);                            \
    VOID                                                            \
    HalpHardwareInterrupt##x(VOID)                                  \
    {                                                               \
        __asm                                                       \
        {                                                           \
            int PRIMARY_VECTOR_BASE + x                             \
        }                                                           \
    }

#else
#error Unsupported compiler
#endif

/* Pending/delayed hardware interrupt handlers */
HalpDelayedHardwareInterrupt(0);
HalpDelayedHardwareInterrupt(1);
HalpDelayedHardwareInterrupt(2);
HalpDelayedHardwareInterrupt(3);
HalpDelayedHardwareInterrupt(4);
HalpDelayedHardwareInterrupt(5);
HalpDelayedHardwareInterrupt(6);
HalpDelayedHardwareInterrupt(7);
HalpDelayedHardwareInterrupt(8);
HalpDelayedHardwareInterrupt(9);
HalpDelayedHardwareInterrupt(10);
HalpDelayedHardwareInterrupt(11);
HalpDelayedHardwareInterrupt(12);
HalpDelayedHardwareInterrupt(13);
HalpDelayedHardwareInterrupt(14);
HalpDelayedHardwareInterrupt(15);

/* Handlers for pending interrupts */
PHAL_SW_INTERRUPT_HANDLER SWInterruptHandlerTable[20] =
{
    KiUnexpectedInterrupt,
    HalpApcInterrupt,
    HalpDispatchInterrupt2,
    KiUnexpectedInterrupt,
    HalpHardwareInterrupt0,
    HalpHardwareInterrupt1,
    HalpHardwareInterrupt2,
    HalpHardwareInterrupt3,
    HalpHardwareInterrupt4,
    HalpHardwareInterrupt5,
    HalpHardwareInterrupt6,
    HalpHardwareInterrupt7,
    HalpHardwareInterrupt8,
    HalpHardwareInterrupt9,
    HalpHardwareInterrupt10,
    HalpHardwareInterrupt11,
    HalpHardwareInterrupt12,
    HalpHardwareInterrupt13,
    HalpHardwareInterrupt14,
    HalpHardwareInterrupt15
};

/* Handlers for pending software interrupts when we already have a trap frame*/
PHAL_SW_INTERRUPT_HANDLER_2ND_ENTRY SWInterruptHandlerTable2[3] =
{
    (PHAL_SW_INTERRUPT_HANDLER_2ND_ENTRY)KiUnexpectedInterrupt,
    HalpApcInterrupt2ndEntry,
    HalpDispatchInterrupt2ndEntry
};

LONG HalpEisaELCR;

/* FUNCTIONS ******************************************************************/

VOID
NTAPI
HalpInitializePICs(IN BOOLEAN EnableInterrupts)
{
    ULONG EFlags;
    I8259_ICW1 Icw1;
    I8259_ICW2 Icw2;
    I8259_ICW3 Icw3;
    I8259_ICW4 Icw4;
    EISA_ELCR Elcr;
    ULONG i, j;

    /* Save EFlags and disable interrupts */
    EFlags = __readeflags();
    _disable();

    /* Initialize ICW1 for master, interval 8, edge-triggered mode with ICW4 */
    Icw1.NeedIcw4 = TRUE;
    Icw1.InterruptMode = EdgeTriggered;
    Icw1.OperatingMode = Cascade;
    Icw1.Interval = Interval8;
    Icw1.Init = TRUE;
    Icw1.InterruptVectorAddress = 0; /* This is only used in MCS80/85 mode */
    __outbyte(PIC1_CONTROL_PORT, Icw1.Bits);

    /* Set interrupt vector base */
    Icw2.Bits = PRIMARY_VECTOR_BASE;
    __outbyte(PIC1_DATA_PORT, Icw2.Bits);

    /* Connect slave to IRQ 2 */
    Icw3.Bits = 0;
    Icw3.SlaveIrq2 = TRUE;
    __outbyte(PIC1_DATA_PORT, Icw3.Bits);

    /* Enable 8086 mode, non-automatic EOI, non-buffered mode, non special fully nested mode */
    Icw4.Reserved = 0;
    Icw4.SystemMode = New8086Mode;
    Icw4.EoiMode = NormalEoi;
    Icw4.BufferedMode = NonBuffered;
    Icw4.SpecialFullyNestedMode = FALSE;
    __outbyte(PIC1_DATA_PORT, Icw4.Bits);

    /* Mask all interrupts */
    __outbyte(PIC1_DATA_PORT, 0xFF);

    /* Initialize ICW1 for master, interval 8, edge-triggered mode with ICW4 */
    Icw1.NeedIcw4 = TRUE;
    Icw1.InterruptMode = EdgeTriggered;
    Icw1.OperatingMode = Cascade;
    Icw1.Interval = Interval8;
    Icw1.Init = TRUE;
    Icw1.InterruptVectorAddress = 0; /* This is only used in MCS80/85 mode */
    __outbyte(PIC2_CONTROL_PORT, Icw1.Bits);

    /* Set interrupt vector base */
    Icw2.Bits = PRIMARY_VECTOR_BASE + 8;
    __outbyte(PIC2_DATA_PORT, Icw2.Bits);

    /* Slave ID */
    Icw3.Bits = 0;
    Icw3.SlaveId = 2;
    __outbyte(PIC2_DATA_PORT, Icw3.Bits);

    /* Enable 8086 mode, non-automatic EOI, non-buffered mode, non special fully nested mode */
    Icw4.Reserved = 0;
    Icw4.SystemMode = New8086Mode;
    Icw4.EoiMode = NormalEoi;
    Icw4.BufferedMode = NonBuffered;
    Icw4.SpecialFullyNestedMode = FALSE;
    __outbyte(PIC2_DATA_PORT, Icw4.Bits);

    /* Mask all interrupts */
    __outbyte(PIC2_DATA_PORT, 0xFF);

    /* Read EISA Edge/Level Register for master and slave */
    Elcr.Bits = (__inbyte(EISA_ELCR_SLAVE) << 8) | __inbyte(EISA_ELCR_MASTER);

    /* IRQs 0, 1, 2, 8, and 13 are system-reserved and must be edge */
    if (!(Elcr.Master.Irq0Level) && !(Elcr.Master.Irq1Level) && !(Elcr.Master.Irq2Level) &&
        !(Elcr.Slave.Irq8Level) && !(Elcr.Slave.Irq13Level))
    {
        /* ELCR is as it's supposed to be, save it */
        HalpEisaELCR = Elcr.Bits;

        /* Scan for level interrupts */
        for (i = 1, j = 0; j < 16; i <<= 1, j++)
        {
            if (HalpEisaELCR & i)
            {
                /* Switch handler to level */
                SWInterruptHandlerTable[j + 4] = HalpHardwareInterruptLevel;

                /* Switch dismiss to level */
                HalpSpecialDismissTable[j] = HalpSpecialDismissLevelTable[j];
            }
        }
    }

    /* Register IRQ 2 */
    HalpRegisterVector(IDT_INTERNAL,
                       PRIMARY_VECTOR_BASE + 2,
                       PRIMARY_VECTOR_BASE + 2,
                       HIGH_LEVEL);

    /* Restore interrupt state */
    if (EnableInterrupts) EFlags |= EFLAGS_INTERRUPT_MASK;
    __writeeflags(EFlags);
}

UCHAR
FASTCALL
HalpIrqToVector(UCHAR Irq)
{
    return (PRIMARY_VECTOR_BASE + Irq);
}

UCHAR
FASTCALL
HalpVectorToIrq(UCHAR Vector)
{
    return (Vector - PRIMARY_VECTOR_BASE);
}

KIRQL
FASTCALL
HalpVectorToIrql(UCHAR Vector)
{
    return (PROFILE_LEVEL - (Vector - PRIMARY_VECTOR_BASE));
}

/* IRQL MANAGEMENT ************************************************************/

/*
 * @implemented
 */
KIRQL
NTAPI
KeGetCurrentIrql(VOID)
{
    /* Return the IRQL */
    return KeGetPcr()->Irql;
}

/*
 * @implemented
 */
KIRQL
NTAPI
KeRaiseIrqlToDpcLevel(VOID)
{
    PKPCR Pcr = KeGetPcr();
    KIRQL CurrentIrql;

    /* Save and update IRQL */
    CurrentIrql = Pcr->Irql;
    Pcr->Irql = DISPATCH_LEVEL;

#if DBG
    /* Validate correct raise */
    if (CurrentIrql > DISPATCH_LEVEL) KeBugCheck(IRQL_NOT_GREATER_OR_EQUAL);
#endif

    /* Return the previous value */
    return CurrentIrql;
}

/*
 * @implemented
 */
KIRQL
NTAPI
KeRaiseIrqlToSynchLevel(VOID)
{
    PKPCR Pcr = KeGetPcr();
    KIRQL CurrentIrql;

    /* Save and update IRQL */
    CurrentIrql = Pcr->Irql;
    Pcr->Irql = SYNCH_LEVEL;

#if DBG
    /* Validate correct raise */
    if (CurrentIrql > SYNCH_LEVEL)
    {
        /* Crash system */
        KeBugCheckEx(IRQL_NOT_GREATER_OR_EQUAL,
                     CurrentIrql,
                     SYNCH_LEVEL,
                     0,
                     1);
    }
#endif

    /* Return the previous value */
    return CurrentIrql;
}

/*
 * @implemented
 */
KIRQL
FASTCALL
KfRaiseIrql(IN KIRQL NewIrql)
{
    PKPCR Pcr = KeGetPcr();
    KIRQL CurrentIrql;

    /* Read current IRQL */
    CurrentIrql = Pcr->Irql;

#if DBG
    /* Validate correct raise */
    if (CurrentIrql > NewIrql)
    {
        /* Crash system */
        Pcr->Irql = PASSIVE_LEVEL;
        KeBugCheck(IRQL_NOT_GREATER_OR_EQUAL);
    }
#endif

    /* Set new IRQL */
    Pcr->Irql = NewIrql;

    /* Return old IRQL */
    return CurrentIrql;
}


/*
 * @implemented
 */
VOID
FASTCALL
KfLowerIrql(IN KIRQL OldIrql)
{
    ULONG EFlags;
    ULONG PendingIrql, PendingIrqlMask;
    PKPCR Pcr = KeGetPcr();
    PIC_MASK Mask;

#if DBG
    /* Validate correct lower */
    if (OldIrql > Pcr->Irql)
    {
        /* Crash system */
        Pcr->Irql = HIGH_LEVEL;
        KeBugCheck(IRQL_NOT_LESS_OR_EQUAL);
    }
#endif

    /* Save EFlags and disable interrupts */
    EFlags = __readeflags();
    _disable();

    /* Set old IRQL */
    Pcr->Irql = OldIrql;

    /* Check for pending software interrupts and compare with current IRQL */
    PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[OldIrql];
    if (PendingIrqlMask)
    {
        /* Check if pending IRQL affects hardware state */
        BitScanReverse(&PendingIrql, PendingIrqlMask);
        if (PendingIrql > DISPATCH_LEVEL)
        {
            /* Set new PIC mask */
            Mask.Both = Pcr->IDR & 0xFFFF;
            __outbyte(PIC1_DATA_PORT, Mask.Master);
            __outbyte(PIC2_DATA_PORT, Mask.Slave);

            /* Clear IRR bit */
            Pcr->IRR ^= (1 << PendingIrql);
        }

        /* Now handle pending interrupt */
        SWInterruptHandlerTable[PendingIrql]();
    }

    /* Restore interrupt state */
    __writeeflags(EFlags);
}

/* SOFTWARE INTERRUPTS ********************************************************/

/*
 * @implemented
 */
VOID
FASTCALL
HalRequestSoftwareInterrupt(IN KIRQL Irql)
{
    ULONG EFlags;
    PKPCR Pcr = KeGetPcr();
    KIRQL PendingIrql;

    /* Save EFlags and disable interrupts */
    EFlags = __readeflags();
    _disable();

    /* Mask out the requested bit */
    Pcr->IRR |= (1 << Irql);

    /* Check for pending software interrupts and compare with current IRQL */
    PendingIrql = SWInterruptLookUpTable[Pcr->IRR & 3];
    if (PendingIrql > Pcr->Irql) SWInterruptHandlerTable[PendingIrql]();

    /* Restore interrupt state */
    __writeeflags(EFlags);
}

/*
 * @implemented
 */
VOID
FASTCALL
HalClearSoftwareInterrupt(IN KIRQL Irql)
{
    /* Mask out the requested bit */
    KeGetPcr()->IRR &= ~(1 << Irql);
}

VOID
NTAPI
HalpEndSoftwareInterrupt(IN KIRQL OldIrql,
                         IN PKTRAP_FRAME TrapFrame)
{
    ULONG PendingIrql, PendingIrqlMask, PendingIrqMask;
    PKPCR Pcr = KeGetPcr();
    PIC_MASK Mask;

    /* Set old IRQL */
    Pcr->Irql = OldIrql;

    /* Loop checking for pending interrupts */
    while (TRUE)
    {
        /* Check for pending software interrupts and compare with current IRQL */
        PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[OldIrql];
        if (!PendingIrqlMask) return;

        /* Check for in-service delayed interrupt */
        if (Pcr->IrrActive & 0xFFFFFFF0) return;

        /* Check if pending IRQL affects hardware state */
        BitScanReverse(&PendingIrql, PendingIrqlMask);
        if (PendingIrql > DISPATCH_LEVEL)
        {
            /* Set new PIC mask */
            Mask.Both = Pcr->IDR & 0xFFFF;
            __outbyte(PIC1_DATA_PORT, Mask.Master);
            __outbyte(PIC2_DATA_PORT, Mask.Slave);

            /* Set active bit otherwise, and clear it from IRR */
            PendingIrqMask = (1 << PendingIrql);
            Pcr->IrrActive |= PendingIrqMask;
            Pcr->IRR ^= PendingIrqMask;

            /* Handle delayed hardware interrupt */
            SWInterruptHandlerTable[PendingIrql]();

            /* Handling complete */
            Pcr->IrrActive ^= PendingIrqMask;
        }
        else
        {
            /* No need to loop checking for hardware interrupts */
            SWInterruptHandlerTable2[PendingIrql](TrapFrame);
        }
    }
}

/* EDGE INTERRUPT DISMISSAL FUNCTIONS *****************************************/

BOOLEAN
FORCEINLINE
_HalpDismissIrqGeneric(IN KIRQL Irql,
                       IN ULONG Irq,
                       OUT PKIRQL OldIrql)
{
    PIC_MASK Mask;
    KIRQL CurrentIrql;
    I8259_OCW2 Ocw2;
    PKPCR Pcr = KeGetPcr();

    /* First save current IRQL and compare it to the requested one */
    CurrentIrql = Pcr->Irql;

    /* Check if this interrupt is really allowed to happen */
    if (Irql > CurrentIrql)
    {
        /* Set the new IRQL and return the current one */
        Pcr->Irql = Irql;
        *OldIrql = CurrentIrql;

        /* Prepare OCW2 for EOI */
        Ocw2.Bits = 0;
        Ocw2.EoiMode = SpecificEoi;

        /* Check which PIC needs the EOI */
        if (Irq > 8)
        {
            /* Send the EOI for the IRQ */
            __outbyte(PIC2_CONTROL_PORT, Ocw2.Bits | ((Irq - 8) & 0xFF));

            /* Send the EOI for IRQ2 on the master because this was cascaded */
            __outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | 2);
        }
        else
        {
            /* Send the EOI for the IRQ */
            __outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | (Irq &0xFF));
        }

        /* Enable interrupts and return success */
        _enable();
        return TRUE;
    }

    /* Update the IRR so that we deliver this interrupt when the IRQL is proper */
    Pcr->IRR |= (1 << (Irq + 4));

    /* Set new PIC mask to real IRQL level, since the optimization is lost now */
    Mask.Both = (KiI8259MaskTable[CurrentIrql] | Pcr->IDR) & 0xFFFF;
    __outbyte(PIC1_DATA_PORT, Mask.Master);
    __outbyte(PIC2_DATA_PORT, Mask.Slave);

    /* Now lie and say this was spurious */
    return FALSE;
}

BOOLEAN
REGISTERCALL
HalpDismissIrqGeneric(IN KIRQL Irql,
                      IN ULONG Irq,
                      OUT PKIRQL OldIrql)
{
    /* Run the inline code */
    return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
}

BOOLEAN
REGISTERCALL
HalpDismissIrq15(IN KIRQL Irql,
                 IN ULONG Irq,
                 OUT PKIRQL OldIrql)
{
    I8259_OCW3 Ocw3;
    I8259_OCW2 Ocw2;
    I8259_ISR Isr;

    /* Request the ISR */
    Ocw3.Bits = 0;
    Ocw3.Sbo = 1; /* This encodes an OCW3 vs. an OCW2 */
    Ocw3.ReadRequest = ReadIsr;
    __outbyte(PIC2_CONTROL_PORT, Ocw3.Bits);

    /* Read the ISR */
    Isr.Bits = __inbyte(PIC2_CONTROL_PORT);

    /* Is IRQ15 really active (this is IR7) */
    if (Isr.Irq7 == FALSE)
    {
        /* It isn't, so we have to EOI IRQ2 because this was cascaded */
        Ocw2.Bits = 0;
        Ocw2.EoiMode = SpecificEoi;
        __outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | 2);

        /* And now fail since this was spurious */
        return FALSE;
    }

    /* Do normal interrupt dismiss */
    return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
}


BOOLEAN
REGISTERCALL
HalpDismissIrq13(IN KIRQL Irql,
                 IN ULONG Irq,
                 OUT PKIRQL OldIrql)
{
    /* Clear the FPU busy latch */
    __outbyte(0xF0, 0);

    /* Do normal interrupt dismiss */
    return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
}

BOOLEAN
REGISTERCALL
HalpDismissIrq07(IN KIRQL Irql,
                 IN ULONG Irq,
                 OUT PKIRQL OldIrql)
{
    I8259_OCW3 Ocw3;
    I8259_ISR Isr;

    /* Request the ISR */
    Ocw3.Bits = 0;
    Ocw3.Sbo = 1;
    Ocw3.ReadRequest = ReadIsr;
    __outbyte(PIC1_CONTROL_PORT, Ocw3.Bits);

    /* Read the ISR */
    Isr.Bits = __inbyte(PIC1_CONTROL_PORT);

    /* Is IRQ 7 really active? If it isn't, this is spurious so fail */
    if (Isr.Irq7 == FALSE) return FALSE;

    /* Do normal interrupt dismiss */
    return _HalpDismissIrqGeneric(Irql, Irq, OldIrql);
}

/* LEVEL INTERRUPT DISMISSAL FUNCTIONS ****************************************/

BOOLEAN
FORCEINLINE
_HalpDismissIrqLevel(IN KIRQL Irql,
                     IN ULONG Irq,
                     OUT PKIRQL OldIrql)
{
    PIC_MASK Mask;
    KIRQL CurrentIrql;
    I8259_OCW2 Ocw2;
    PKPCR Pcr = KeGetPcr();

    /* Update the PIC */
    Mask.Both = (KiI8259MaskTable[Irql] | Pcr->IDR) & 0xFFFF;
    __outbyte(PIC1_DATA_PORT, Mask.Master);
    __outbyte(PIC2_DATA_PORT, Mask.Slave);

    /* Update the IRR so that we clear this interrupt when the IRQL is proper */
    Pcr->IRR |= (1 << (Irq + 4));

    /* Save current IRQL */
    CurrentIrql = Pcr->Irql;

    /* Prepare OCW2 for EOI */
    Ocw2.Bits = 0;
    Ocw2.EoiMode = SpecificEoi;

    /* Check which PIC needs the EOI */
    if (Irq > 8)
    {
        /* Send the EOI for the IRQ */
        __outbyte(PIC2_CONTROL_PORT, Ocw2.Bits | ((Irq - 8) & 0xFF));

        /* Send the EOI for IRQ2 on the master because this was cascaded */
        __outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | 2);
    }
    else
    {
        /* Send the EOI for the IRQ */
        __outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | (Irq & 0xFF));
    }

    /* Check if this interrupt should be allowed to happen */
    if (Irql > CurrentIrql)
    {
        /* Set the new IRQL and return the current one */
        Pcr->Irql = Irql;
        *OldIrql = CurrentIrql;

        /* Enable interrupts and return success */
        _enable();
        return TRUE;
    }

    /* Now lie and say this was spurious */
    return FALSE;
}

BOOLEAN
REGISTERCALL
HalpDismissIrqLevel(IN KIRQL Irql,
                    IN ULONG Irq,
                    OUT PKIRQL OldIrql)
{
    /* Run the inline code */
    return _HalpDismissIrqLevel(Irql, Irq, OldIrql);
}

BOOLEAN
REGISTERCALL
HalpDismissIrq15Level(IN KIRQL Irql,
                      IN ULONG Irq,
                      OUT PKIRQL OldIrql)
{
    I8259_OCW3 Ocw3;
    I8259_OCW2 Ocw2;
    I8259_ISR Isr;

    /* Request the ISR */
    Ocw3.Bits = 0;
    Ocw3.Sbo = 1; /* This encodes an OCW3 vs. an OCW2 */
    Ocw3.ReadRequest = ReadIsr;
    __outbyte(PIC2_CONTROL_PORT, Ocw3.Bits);

    /* Read the ISR */
    Isr.Bits = __inbyte(PIC2_CONTROL_PORT);

    /* Is IRQ15 really active (this is IR7) */
    if (Isr.Irq7 == FALSE)
    {
        /* It isn't, so we have to EOI IRQ2 because this was cascaded */
        Ocw2.Bits = 0;
        Ocw2.EoiMode = SpecificEoi;
        __outbyte(PIC1_CONTROL_PORT, Ocw2.Bits | 2);

        /* And now fail since this was spurious */
        return FALSE;
    }

    /* Do normal interrupt dismiss */
    return _HalpDismissIrqLevel(Irql, Irq, OldIrql);
}

BOOLEAN
REGISTERCALL
HalpDismissIrq13Level(IN KIRQL Irql,
                      IN ULONG Irq,
                      OUT PKIRQL OldIrql)
{
    /* Clear the FPU busy latch */
    __outbyte(0xF0, 0);

    /* Do normal interrupt dismiss */
    return _HalpDismissIrqLevel(Irql, Irq, OldIrql);
}

BOOLEAN
REGISTERCALL
HalpDismissIrq07Level(IN KIRQL Irql,
                      IN ULONG Irq,
                      OUT PKIRQL OldIrql)
{
    I8259_OCW3 Ocw3;
    I8259_ISR Isr;

    /* Request the ISR */
    Ocw3.Bits = 0;
    Ocw3.Sbo = 1;
    Ocw3.ReadRequest = ReadIsr;
    __outbyte(PIC1_CONTROL_PORT, Ocw3.Bits);

    /* Read the ISR */
    Isr.Bits = __inbyte(PIC1_CONTROL_PORT);

    /* Is IRQ 7 really active? If it isn't, this is spurious so fail */
    if (Isr.Irq7 == FALSE) return FALSE;

    /* Do normal interrupt dismiss */
    return _HalpDismissIrqLevel(Irql, Irq, OldIrql);
}

VOID
HalpHardwareInterruptLevel(VOID)
{
    PKPCR Pcr = KeGetPcr();
    ULONG PendingIrqlMask, PendingIrql;

    /* Check for pending software interrupts and compare with current IRQL */
    PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[Pcr->Irql];
    if (PendingIrqlMask)
    {
        /* Check for in-service delayed interrupt */
        if (Pcr->IrrActive & 0xFFFFFFF0) return;

        /* Check if pending IRQL affects hardware state */
        BitScanReverse(&PendingIrql, PendingIrqlMask);

        /* Clear IRR bit */
        Pcr->IRR ^= (1 << PendingIrql);

        /* Now handle pending interrupt */
        SWInterruptHandlerTable[PendingIrql]();
    }
}

/* SYSTEM INTERRUPTS **********************************************************/

/*
 * @implemented
 */
BOOLEAN
NTAPI
HalEnableSystemInterrupt(IN ULONG Vector,
                         IN KIRQL Irql,
                         IN KINTERRUPT_MODE InterruptMode)
{
    ULONG Irq;
    PKPCR Pcr = KeGetPcr();
    PIC_MASK PicMask;

    /* Validate the IRQ */
    Irq = Vector - PRIMARY_VECTOR_BASE;
    if (Irq >= CLOCK2_LEVEL) return FALSE;

    /* Check for level interrupt */
    if (InterruptMode == LevelSensitive)
    {
        /* Switch handler to level */
        SWInterruptHandlerTable[Irq + 4] = HalpHardwareInterruptLevel;

        /* Switch dismiss to level */
        HalpSpecialDismissTable[Irq] = HalpSpecialDismissLevelTable[Irq];
    }

    /* Disable interrupts */
    _disable();

    /* Update software IDR */
    Pcr->IDR &= ~(1 << Irq);

    /* Set new PIC mask */
    PicMask.Both = (KiI8259MaskTable[Pcr->Irql] | Pcr->IDR) & 0xFFFF;
    __outbyte(PIC1_DATA_PORT, PicMask.Master);
    __outbyte(PIC2_DATA_PORT, PicMask.Slave);

    /* Enable interrupts and exit */
    _enable();
    return TRUE;
}

/*
 * @implemented
 */
VOID
NTAPI
HalDisableSystemInterrupt(IN ULONG Vector,
                          IN KIRQL Irql)
{
    ULONG IrqMask;
    PIC_MASK PicMask;

    /* Compute new combined IRQ mask */
    IrqMask = 1 << (Vector - PRIMARY_VECTOR_BASE);

    /* Disable interrupts */
    _disable();

    /* Update software IDR */
    KeGetPcr()->IDR |= IrqMask;

    /* Read current interrupt mask */
    PicMask.Master = __inbyte(PIC1_DATA_PORT);
    PicMask.Slave = __inbyte(PIC2_DATA_PORT);

    /* Add the new disabled interrupt */
    PicMask.Both |= IrqMask;

    /* Write new interrupt mask */
    __outbyte(PIC1_DATA_PORT, PicMask.Master);
    __outbyte(PIC2_DATA_PORT, PicMask.Slave);

    /* Bring interrupts back */
    _enable();
}

/*
 * @implemented
 */
BOOLEAN
NTAPI
HalBeginSystemInterrupt(IN KIRQL Irql,
                        IN ULONG Vector,
                        OUT PKIRQL OldIrql)
{
    ULONG Irq;

    /* Get the IRQ and call the proper routine to handle it */
    Irq = Vector - PRIMARY_VECTOR_BASE;
    return HalpSpecialDismissTable[Irq](Irql, Irq, OldIrql);
}

/*
 * @implemented
 */
VOID
NTAPI
HalEndSystemInterrupt(IN KIRQL OldIrql,
                      IN PKTRAP_FRAME TrapFrame)
{
    ULONG PendingIrql, PendingIrqlMask, PendingIrqMask;
    PKPCR Pcr = KeGetPcr();
    PIC_MASK Mask;

    /* Set old IRQL */
    Pcr->Irql = OldIrql;

    /* Check for pending software interrupts and compare with current IRQL */
    PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[OldIrql];
    if (PendingIrqlMask)
    {
        /* Check for in-service delayed interrupt */
        if (Pcr->IrrActive & 0xFFFFFFF0) return;

        /* Loop checking for pending interrupts */
        while (TRUE)
        {
            /* Check if pending IRQL affects hardware state */
            BitScanReverse(&PendingIrql, PendingIrqlMask);
            if (PendingIrql > DISPATCH_LEVEL)
            {
                /* Set new PIC mask */
                Mask.Both = Pcr->IDR & 0xFFFF;
                __outbyte(PIC1_DATA_PORT, Mask.Master);
                __outbyte(PIC2_DATA_PORT, Mask.Slave);

                /* Now check if this specific interrupt is already in-service */
                PendingIrqMask = (1 << PendingIrql);
                if (Pcr->IrrActive & PendingIrqMask) return;

                /* Set active bit otherwise, and clear it from IRR */
                Pcr->IrrActive |= PendingIrqMask;
                Pcr->IRR ^= PendingIrqMask;

                /* Handle delayed hardware interrupt */
                SWInterruptHandlerTable[PendingIrql]();

                /* Handling complete */
                Pcr->IrrActive ^= PendingIrqMask;

                /* Check if there's still interrupts pending */
                PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[Pcr->Irql];
                if (!PendingIrqlMask) break;
            }
            else
            {
                /* Now handle pending software interrupt */
                SWInterruptHandlerTable2[PendingIrql](TrapFrame);
            }
        }
    }
}

/* SOFTWARE INTERRUPT TRAPS ***************************************************/

VOID
FORCEINLINE
DECLSPEC_NORETURN
_HalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame)
{
    KIRQL CurrentIrql;
    PKPCR Pcr = KeGetPcr();

    /* Save the current IRQL and update it */
    CurrentIrql = Pcr->Irql;
    Pcr->Irql = APC_LEVEL;

    /* Remove DPC from IRR */
    Pcr->IRR &= ~(1 << APC_LEVEL);

    /* Enable interrupts and call the kernel's APC interrupt handler */
    _enable();
    KiDeliverApc(((KiUserTrap(TrapFrame)) || (TrapFrame->EFlags & EFLAGS_V86_MASK)) ?
                UserMode : KernelMode,
                NULL,
                TrapFrame);

    /* Disable interrupts and end the interrupt */
    _disable();
    HalpEndSoftwareInterrupt(CurrentIrql, TrapFrame);

    /* Exit the interrupt */
    KiEoiHelper(TrapFrame);
}

VOID
DECLSPEC_NORETURN
FASTCALL
HalpApcInterrupt2ndEntry(IN PKTRAP_FRAME TrapFrame)
{
    /* Do the work */
    _HalpApcInterruptHandler(TrapFrame);
}

VOID
DECLSPEC_NORETURN
FASTCALL
HalpApcInterruptHandler(IN PKTRAP_FRAME TrapFrame)
{
    /* Set up a fake INT Stack */
    TrapFrame->EFlags = __readeflags();
    TrapFrame->SegCs = KGDT_R0_CODE;
    TrapFrame->Eip = TrapFrame->Eax;

    /* Build the trap frame */
    KiEnterInterruptTrap(TrapFrame);

    /* Do the work */
    _HalpApcInterruptHandler(TrapFrame);
}

KIRQL
FORCEINLINE
_HalpDispatchInterruptHandler(VOID)
{
    KIRQL CurrentIrql;
    PKPCR Pcr = KeGetPcr();

    /* Save the current IRQL and update it */
    CurrentIrql = Pcr->Irql;
    Pcr->Irql = DISPATCH_LEVEL;

    /* Remove DPC from IRR */
    Pcr->IRR &= ~(1 << DISPATCH_LEVEL);

    /* Enable interrupts and call the kernel's DPC interrupt handler */
    _enable();
    KiDispatchInterrupt();
    _disable();

    /* Return IRQL */
    return CurrentIrql;
}

VOID
DECLSPEC_NORETURN
FASTCALL
HalpDispatchInterrupt2ndEntry(IN PKTRAP_FRAME TrapFrame)
{
    KIRQL CurrentIrql;

    /* Do the work */
    CurrentIrql = _HalpDispatchInterruptHandler();

    /* End the interrupt */
    HalpEndSoftwareInterrupt(CurrentIrql, TrapFrame);

    /* Exit the interrupt */
    KiEoiHelper(TrapFrame);
}

VOID
HalpDispatchInterrupt2(VOID)
{
    ULONG PendingIrqlMask, PendingIrql;
    KIRQL OldIrql;
    PIC_MASK Mask;
    PKPCR Pcr = KeGetPcr();

    /* Do the work */
    OldIrql = _HalpDispatchInterruptHandler();

    /* Restore IRQL */
    Pcr->Irql = OldIrql;

    /* Check for pending software interrupts and compare with current IRQL */
    PendingIrqlMask = Pcr->IRR & FindHigherIrqlMask[OldIrql];
    if (PendingIrqlMask)
    {
        /* Check if pending IRQL affects hardware state */
        BitScanReverse(&PendingIrql, PendingIrqlMask);
        if (PendingIrql > DISPATCH_LEVEL)
        {
            /* Set new PIC mask */
            Mask.Both = Pcr->IDR & 0xFFFF;
            __outbyte(PIC1_DATA_PORT, Mask.Master);
            __outbyte(PIC2_DATA_PORT, Mask.Slave);

            /* Clear IRR bit */
            Pcr->IRR ^= (1 << PendingIrql);
        }

        /* Now handle pending interrupt */
        SWInterruptHandlerTable[PendingIrql]();
    }
}

#else

KIRQL
NTAPI
KeGetCurrentIrql(VOID)
{
    return PASSIVE_LEVEL;
}

VOID
FASTCALL
KfLowerIrql(
    IN KIRQL OldIrql)
{
}

KIRQL
FASTCALL
KfRaiseIrql(
    IN KIRQL NewIrql)
{
    return NewIrql;
}

#endif
